/*
XBaseTools
Copyright (C) 2008 Matthew Perry

This library/program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This libary/program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using BaseTools.Searching;

namespace BaseTools.Configuration
{
	/// <summary>
	/// A nested configuration list/structure
	/// </summary>
	public class ConfigList : System.Collections.Generic.IEnumerable<ConfigPair>, ICloneable
	{
		#region "Members"
		
		private List<ConfigPair> listPairs;
		
		#endregion
		
		#region "Constructors"
		
		/// <summary>
		/// Creates a new ConfigList
		/// </summary>
		public ConfigList()
		{
			listPairs = new List<ConfigPair>();
		}
		
		/// <summary>
		/// Creates a new ConfigList
		/// </summary>
		/// <param name="pairs">The initial pairs</param>
		public ConfigList(List<ConfigPair> pairs)
		{
			listPairs = pairs;
		}
		
		#endregion
		
		#region "Less important stuff"
		/// <summary>
		/// Creates a clone of this ConfigList
		/// </summary>
		/// <returns>A clone of this ConfigList</returns>
		public Object Clone()
		{
			List<ConfigPair> newPairs = new List<ConfigPair>();
			
			foreach(ConfigPair p in listPairs)
				newPairs.Add((ConfigPair)p.Clone());
			
			return new ConfigList(newPairs);
		}
		
		/// <summary>
		/// Merges this list's strings with another
		/// </summary>
		/// <param name="list">The list to merge with</param>
		public void MergeStrings(ConfigList list)
		{
			foreach(ConfigPair p in list.FindPairs(new PairStringValidator()))
			{
				if (this.HasString(new StringValidator(p.Key, false)))
					this.SetString(new StringValidator(p.Key, false), p.DataAsString);
				else
					this.AddString(p.Key, p.DataAsString);
			}
		}
		
		/// <summary>
		/// Creates an enumerator for this ConfigList
		/// </summary>
		/// <returns>Enumerator</returns>
		public System.Collections.Generic.IEnumerator<ConfigPair> GetEnumerator()
		{
			return listPairs.GetEnumerator();
			/*foreach(ConfigPair p in listPairs)
				yield return p;*/
		}
		
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
		
		/// <summary>
		/// Finds data by a path
		/// </summary>
		/// <param name="rel">The place the path is relative to</param>
		/// <param name="path">The path</param>
		/// <returns>The path object or null</returns>
		public static Object FindPath(ConfigList rel, string path)
		{
			if (path.Length == 0 || path == "/")
				return rel;
			
			string[] splitPath = path.Split('/');
			
			for(int i=0;i<splitPath.Length;i++)
			{
				string s = splitPath[i];
				
				rel = rel.GetSection(s);
				
				if (rel == null)
				{
					if (i == splitPath.Length-1)
						return rel.GetString(s);
					else
						return null;
				}
			}
			
			return rel;
		}
		#endregion
			
		#region "Adding"
		
		/// <summary>
		/// Adds a pair directly into the ConfigList
		/// </summary>
		/// <param name="p">The pair to add</param>
		public void AddPair(ConfigPair p)
		{
			listPairs.Add(p);
		}
		
		/// <summary>
		/// Adds a string/value
		/// </summary>
		/// <param name="key">The key</param>
		/// <param name="data">The data</param>
		public void AddString(string key, string data)
		{
			listPairs.Add(new ConfigPair(key, data));
		}
		
		/// <summary>
		/// Adds a section
		/// </summary>
		/// <param name="key">The section key</param>
		/// <param name="section">The section</param>
		public void AddSection(string key, ConfigList section)
		{
			listPairs.Add(new ConfigPair(key, section));
		}
		
		/// <summary>
		/// Adds a section
		/// </summary>
		/// <param name="key">The section key</param>
		public void AddSection(string key)
		{
			listPairs.Add(new ConfigPair(key, new ConfigList()));
		}
		
		#endregion
		
		#region "Searching Pairs"
		
		/// <summary>
		/// Finds the first pair that matches the specified validator
		/// </summary>
		/// <param name="validator">The validator to use</param>
		/// <returns>The first pair matching the validator</returns>
		public ConfigPair FindPair(IValidator<string> validator)
		{
			foreach(ConfigPair p in listPairs)
			{
				if (validator.ItemValid(p.Key))
					return p;
			}
			
			return null;
		}
		
		/// <summary>
		/// Finds the first pair that matches the specified validator
		/// </summary>
		/// <param name="validator">The validator to use</param>
		/// <returns>The first pair matching the validator</returns>
		public ConfigPair FindPair(IValidator<ConfigPair> validator)
		{
			foreach(ConfigPair p in listPairs)
			{
				if (validator.ItemValid(p))
					return p;
			}
			
			return null;
		}
		
		/// <summary>
		/// Finds all of the pairs that match the specified validator
		/// (this method is here to retain consistency with Java XBaseTools)
		/// </summary>
		/// <param name="validator">The validator to use</param>
		/// <returns>A list of pairs matching the validator</returns>
		public List<ConfigPair> FindPairsV(IValidator<ConfigPair> validator)
		{
			return FindPairs(validator);
		}
		
		/// <summary>
		/// Finds all of the pairs that match the specified validator
		/// </summary>
		/// <param name="validator">The validator to use</param>
		/// <returns>A list of pairs matching the validator</returns>
		public List<ConfigPair> FindPairs(IValidator<ConfigPair> validator)
		{
			List<ConfigPair> pairs = new List<ConfigPair>();
			
			foreach(ConfigPair p in listPairs)
			{
				if (validator.ItemValid(p))
					pairs.Add(p);
			}
			
			return pairs;
		}
		
		/// <summary>
		/// Finds all of the pairs that match the specified validator
		/// </summary>
		/// <param name="validator">The validator to use</param>
		/// <returns>A list of pairs matching the validator</returns>
		public List<ConfigPair> FindPairs(IValidator<string> validator)
		{
			List<ConfigPair> pairs = new List<ConfigPair>();
			
			foreach(ConfigPair p in listPairs)
			{
				if (validator.ItemValid(p.Key))
					pairs.Add(p);
			}
			
			return pairs;
		}
		
		/// <summary>
		/// Finds all of the pairs with the specified key
		/// </summary>
		/// <param name="key">The key to search for</param>
		/// <returns>A list of pairs with the key</returns>
		public List<ConfigPair> FindPairs(string key)
		{
			return FindPairs(new StringValidator(key));
		}
		
		/// <summary>
		/// Finds all of the pairs with the specified key
		/// </summary>
		/// <param name="key">The key to search for</param>
		/// <param name="caseSensitive">Whether the search is case-sensitive</param>
		/// <returns>A list of pairs with the key</returns>
		public List<ConfigPair> FindPairs(string key, bool caseSensitive)
		{
			return FindPairs(new StringValidator(key, caseSensitive));
		}
		
		/// <summary>
		/// Finds all of the pairs that match the specified validator
		/// </summary>
		/// <param name="validator">The validator to use</param>
		/// <returns>A list of indexes</returns>
		public List<int> FindPairsIndex(IValidator<ConfigPair> validator)
		{
			List<int> matches = new List<int>();
			int i = 0;
			foreach(ConfigPair p in listPairs)
			{
				if (validator.ItemValid(p))
					matches.Add(i);
				i++;
			}
			
			return matches;
		}
		
		/// <summary>
		/// Finds all of the pairs that match the specified validator
		/// </summary>
		/// <param name="validator">The validator to use</param>
		/// <returns>A list of indexes</returns>
		public List<int> FindPairsIndex(IValidator<string> validator)
		{
			List<int> matches = new List<int>();
			int i = 0;
			foreach(ConfigPair p in listPairs)
			{
				if (validator.ItemValid(p.Key))
					matches.Add(i);
				i++;
			}
			
			return matches;
		}
		
		/// <summary>
		/// Finds all of the pairs with the specified key value
		/// </summary>
		/// <param name="key">The key to search for</param>
		/// <returns>A list of indexes</returns>
		public List<int> FindPairsIndex(string key)
		{
			return FindPairsIndex(new StringValidator(key));
		}
		
		/// <summary>
		/// Finds all the pairs with the specified key value
		/// </summary>
		/// <param name="key">The key to search for</param>
		/// <param name="caseSensitive">Whether the search is case-sensitive</param>
		/// <returns>A list of indexes</returns>
		public List<int> FindPairsIndex(string key, bool caseSensitive)
		{
			return FindPairsIndex(new StringValidator(key, caseSensitive));
		}
		
		#endregion
		
		#region "Searching Items"
		
		/// <summary>
		/// Finds all of the sections that match the specified validator
		/// </summary>
		/// <param name="validator">The validator to use</param>
		/// <returns>A list of sections matching the validator</returns>
		public List<ConfigList> GetSections(IValidator<string> validator)
		{
			List<ConfigList> lists = new List<ConfigList>();
			foreach(ConfigPair p in listPairs)
			{
				if (p.DataIsList && validator.ItemValid(p.Key))
					lists.Add(p.DataAsList);
			}
			
			return lists;
		}
		
		/// <summary>
		/// Checks that a section exists
		/// </summary>
		/// <param name="validator">The validator to use</param>
		/// <returns>Whether any sections are valid</returns>
		public bool HasSection(IValidator<string> validator)
		{
			foreach(ConfigPair p in listPairs)
			{
				if (p.DataIsList && validator.ItemValid(p.Key))
					return true;
			}
			
			return false;
		}
		
		/// <summary>
		/// Finds the first section that matches the specified validator
		/// </summary>
		/// <param name="validator">The validator to use</param>
		/// <returns>The first section matching the validator or null if none match</returns>
		public ConfigList GetSection(IValidator<string> validator)
		{
			foreach(ConfigPair p in listPairs)
			{
				if (p.DataIsList && validator.ItemValid(p.Key))
					return p.DataAsList;
			}
			
			return null;
		}
		
		/// <summary>
		/// Finds the first section with the key
		/// </summary>
		/// <param name="key">The key to search for</param>
		/// <returns>The first section with the key or null if not found</returns>
		public ConfigList GetSection(string key)
		{
			return GetSection(new StringValidator(key));
		}
		
		/// <summary>
		/// Finds the first section with the key
		/// </summary>
		/// <param name="key">The key to search for</param>
		/// <param name="caseSensitive">Whether the search is case-sensitive</param>
		/// <returns>The first section with the key or null if not found</returns>
		public ConfigList GetSection(string key, bool caseSensitive)
		{
			return GetSection(new StringValidator(key, caseSensitive));
		}
		
		/// <summary>
		/// Finds all of the strings matching the validator
		/// </summary>
		/// <param name="validator">The validator to use</param>
		/// <returns>A list of strings matching the validator</returns>
		public List<string> GetStrings(IValidator<string> validator)
		{
			List<string> pairs = new List<string>();
			foreach(ConfigPair p in listPairs)
			{
				if (p.DataIsString && validator.ItemValid(p.Key))
					pairs.Add(p.DataAsString);
			}
			
			return pairs;
		}
		
		/// <summary>
		/// Checks that the pair exists
		/// </summary>
		/// <param name="validator">The validator to use</param>
		/// <returns>Whether any strings are null</returns>
		public bool HasPair(IValidator<string> validator)
		{
			foreach(ConfigPair p in listPairs)
			{
				if (validator.ItemValid(p.Key))
					return true;
			}
			
			return false;
		}
		
		/// <summary>
		/// Checks that the string exists
		/// </summary>
		/// <param name="validator">The validator to use</param>
		/// <returns>Whether any strings are null</returns>
		public bool HasString(IValidator<string> validator)
		{
			foreach(ConfigPair p in listPairs)
			{
				if (p.DataIsString && validator.ItemValid(p.Key))
					return true;
			}
			
			return false;
		}
		
		/// <summary>
		/// Finds the first pair matching the validator
		/// </summary>
		/// <param name="validator">The validator to use</param>
		/// <returns>The first pair matching the validator or null if none match</returns>
		public ConfigPair GetPair(IValidator<string> validator)
		{
			foreach(ConfigPair p in listPairs)
			{
				if (validator.ItemValid(p.Key))
					return p;
			}
			
			return null;
		}
		
		/// <summary>
		/// Finds the first string matching the validator
		/// </summary>
		/// <param name="validator">The validator to use</param>
		/// <returns>The first string matching the validatoor or null if none match</returns>
		public string GetString(IValidator<string> validator)
		{
			foreach(ConfigPair p in listPairs)
			{
				if (p.DataIsString && validator.ItemValid(p.Key))
					return p.DataAsString;
			}
			
			return null;
		}
		
		/// <summary>
		/// Finds the first string with the key
		/// </summary>
		/// <param name="key">The key to search for</param>
		/// <returns>The first string with the key or null if not found</returns>
		public string GetString(string key)
		{
			return GetString(new StringValidator(key));
		}
		
		/// <summary>
		/// Finds the first string with the key
		/// </summary>
		/// <param name="key">The key to search for</param>
		/// <param name="caseSensitive">Whether the search is case-sensitive</param>
		/// <returns>The first string with the key or null if not found</returns>
		public string GetString(string key, bool caseSensitive)
		{
			return GetString(new StringValidator(key, caseSensitive));
		}
		
		#endregion
		
		#region "Setting items"
		
		/// <summary>
		/// Sets the first string that matched the validator
		/// </summary>
		/// <param name="validator">The validator to use</param>
		/// <param name="val">The new value for the string</param>
		/// <returns>Whether there was any valid string to change</returns>
		public bool SetPair(IValidator<string> validator, ConfigPair val)
		{
			for(int i=0;i<listPairs.Count;i++)
			{
				ConfigPair p = listPairs[i];
				if (validator.ItemValid(p.Key))
				{
					listPairs[i] = val;
					return true;
				}
			}
			
			return false;
		}
		
		/// <summary>
		/// Sets the first pair with the key to val
		/// </summary>
		/// <param name="key">The key to search for</param>
		/// <param name="val">The new value for the string</param>
		/// <returns>Whether there was any valid string to change</returns>
		public bool SetPair(string key, ConfigPair val)
		{
			return SetPair(key, val, true);
		}
		
		/// <summary>
		/// Sets the first pair with the key to val
		/// </summary>
		/// <param name="key">The key to search for</param>
		/// <param name="val">The new value for the string</param>
		/// <param name="caseSensitive">Case sensitive keyname</param>
		/// <returns>Whether there was any valid string to change</returns>
		public bool SetPair(string key, ConfigPair val, bool caseSensitive)
		{
			if (!HasPair(new StringValidator(key, caseSensitive)))
			{
				AddPair(val);
				return true;
			}
			else
			{
				return SetPair(new StringValidator(key, caseSensitive), val);
			}
		}
		
		/// <summary>
		/// Sets the first string that matched the validator
		/// </summary>
		/// <param name="validator">The validator to use</param>
		/// <param name="val">The new value for the string</param>
		/// <returns>Whether there was any valid string to change</returns>
		public bool SetString(IValidator<string> validator, string val)
		{
			foreach(ConfigPair p in listPairs)
			{
				if (validator.ItemValid(p.Key))
				{
					p.Data = val;
					return true;
				}
			}
			
			return false;
		}
		
		/// <summary>
		/// Sets the first string with the key to val
		/// </summary>
		/// <param name="key">The key to search for</param>
		/// <param name="val">The new value for the string</param>
		/// <returns>Whether there was any valid string to change</returns>
		public bool SetString(string key, string val)
		{
			return SetString(key, val, true);
		}
		
		/// <summary>
		/// Sets the first string with the key to val
		/// </summary>
		/// <param name="key">The key to search for</param>
		/// <param name="val">The new value for the string</param>
		/// <param name="caseSensitive">Case sensitive keyname</param>
		/// <returns>Whether there was any valid string to change</returns>
		public bool SetString(string key, string val, bool caseSensitive)
		{
			if (!HasString(new StringValidator(key, caseSensitive)))
			{
				AddString(key, val);
				return true;
			}
			else
			{
				return SetString(new StringValidator(key, caseSensitive), val);
			}
		}
		
		#endregion
		
		#region "Removal"
		
		/// <summary>
		/// Remove an item from the list
		/// </summary>
		/// <param name="index">Index to remove</param>
		public void Remove(int index)
		{
			listPairs.RemoveAt(index);
		}
		
		private bool RemoveOne(IValidator<string> validator)
		{
			for(int i=0;i<listPairs.Count;i++)
			{
				if (validator.ItemValid(listPairs[i].Key))
				{
					listPairs.RemoveAt(i);
					return true;
				}
			}
			return false;
		}
		
		/// <summary>
		/// Remove items from the list by validator
		/// </summary>
		/// <param name="validator">The validator to use to delete</param>
		public void RemoveKey(IValidator<string> validator)
		{
			while(RemoveOne(validator));
		}
		
		/// <summary>
		/// Remove items from the list by key
		/// </summary>
		/// <param name="key">The key to delete</param>
		public void RemoveKey(string key)
		{
			RemoveKey(new StringValidator(key));
		}
		
		/// <summary>
		/// Remove items from the list by key
		/// </summary>
		/// <param name="key">The key to delete</param>
		/// <param name="caseSensitive">Case sensitive keyname</param>
		public void RemoveKey(string key, bool caseSensitive)
		{
			RemoveKey(new StringValidator(key, caseSensitive));
		}
		
		/// <summary>
		/// Remove an item from the list by reference
		/// </summary>
		/// <param name="p">The reference</param>
		public void RemoveReference(ConfigPair p)
		{
			listPairs.Remove(p);
		}
		
		#endregion
		
		#region Misc
		
		/// <summary>
		/// Get a list of all the pairs
		/// </summary>
		/// <returns>A list of the pairs</returns>
		public List<ConfigPair> GetList()
		{
			return listPairs;
		}
		
		/// <summary>
		/// Appends another list to this one
		/// </summary>
		/// <param name="list">The list to append</param>
		public void AppendList(ConfigList list)
		{
			foreach(ConfigPair p in list.listPairs)
				listPairs.Add((ConfigPair)p.Clone());
		}
		
		/// <summary>
		/// Case-insensitive operator for data items
		/// </summary>
		public string this[string index]
		{
			get
			{
				return GetString(index, false);
			}
			set
			{
				SetString(index, value, false);
			}
		}
		
		#endregion
	}
}
